/**
 * 
 */
package menus.demm;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Vector;

import components.intertitles.Trial;
import components.intertitles.TrialInstructions;

import fr.inria.insitu.noe.Platform;
import fr.lri.swingstates.canvas.CPolyLine;
import fr.lri.swingstates.canvas.CStateMachine;
import fr.lri.swingstates.canvas.Canvas;
import fr.lri.swingstates.gestures.Gesture;
import fr.lri.swingstates.sm.State;
import fr.lri.swingstates.sm.Transition;
import fr.lri.swingstates.sm.transitions.Drag;
import fr.lri.swingstates.sm.transitions.Press;
import fr.lri.swingstates.sm.transitions.Release;
import menus.common.AbstractItem;
import menus.common.AbstractMenu;
import menus.common.statemachines.IStateMachine;

/**
 * @author insitu
 *
 */
public class DEMMStateMachine extends CStateMachine implements IStateMachine {
	
	/**
	 * Label for the "inactive" state
	 */
	protected static final String INACTIVE_STATE = "inactif";
	
	/**
	 * Label for the "selection" state
	 */
	protected static final String SELECTION_STATE = "selection";
	
	/**
	 * Label for the "triggered" state
	 */
	protected static final String TRIGGERED_STATE = "triggered";
	
	
	
	/**
	 * Menu to which is attached this state machine
	 */
	protected AbstractMenu menu;
	
	/**
	 * Gesture made by the user, used to determine the selected item (if any)
	 */
	protected Gesture gesture;
	
	/**
	 * Mark drawn by the user. Only for visual feedback.
	 */
	protected CPolyLine mark;
	
	/**
	 * Location of the menu for its last call.
	 */
	protected Point2D where;
	
	/**
	 * Selected item
	 */
	protected AbstractItem selected;
	
	/**
	 * Temporal threshold. 
	 * TODO Not used right now
	 */
	protected int startTreshold = 1000;
	
	/**
	 * Timestamp of the selection beginning
	 */
	protected long selectionStart = -1;
	
	/**
	 * Duration of the selection
	 */
	protected long selectionDuration = -1;
	
	/**
	 * False if a selection is occurring, true either
	 * Used for the experiment
	 */
	protected boolean selectionEnded = false;
	
	protected CPolyLine currentMark;
	protected ArrayList<CPolyLine> marks;
	
	protected double[] frontiers;
	
	protected int currentFrontier = 0;
	
	
	
	
	/**
	 * Initial state. Waits for the selection to begin.
	 */
	public State inactive = new State(INACTIVE_STATE){
		
		public Transition startSelection = new Press(">> " + TRIGGERED_STATE){
			
			@Override
			public void action() {
				
				gesture.addPoint(getPoint().getX(), getPoint().getY());
				
				where = getPoint();
				
				currentMark = menu.getCanvas().newPolyLine(where);
				currentMark.setFilled(false)
							.setOutlinePaint(((DEMM)menu).getColors()[0])
							.setStroke(mark.getStroke())
				
							.setDrawable(true)
							.setPickable(false);
	
				marks.add(currentMark);
				
				selectionStart = System.currentTimeMillis();
				
				menu.getCanvas().removeShapes(menu.getCanvas().getTag(TrialInstructions.InstructionTag));
				
			}
			
		};
		
		@Override
		public void enter() {
			
			// Resetting the gesture and hiding the mark
			gesture.reset();
			mark.setDrawable(false);
			
			menu.getCanvas().repaint();
			
		}
		
	};
	
	/**
	 * Intermediate state. Used to differentiate the different selection ways
	 * (one-stroke, tap-and-stroke, etc.)
	 */
	public State triggered = new State(TRIGGERED_STATE){
		
		public Transition startStroke = new Drag(">> " + SELECTION_STATE){
			@Override
			public void action() {
				
				mark.lineTo(getPoint());
				gesture.addPoint(getPoint().getX(), getPoint().getY());
			}
		};
		
		public Transition simpleCancel = new Release(">> " + INACTIVE_STATE){};
		
	};
	
	public State selection = new State(SELECTION_STATE){
		
		public Transition drag = new Drag(){
			
			int lastPoint;
			Vector<Point2D> points;
			
			Point2D p1, p0;
			
			int lastFrontier;
			int lastPointFrontier;
			
			@Override
			public void action() {
				
				lastFrontier = frontiers.length-1;
				
				if (currentMark == null)
					mark.lineTo(getPoint());
				else
					currentMark.lineTo(getPoint());
				
				gesture.addPoint(getPoint().getX(), getPoint().getY());
//				
//				points = gesture.getPoints();
//				lastPoint = points.size()-1;
//				
//				p1 = points.get(lastPoint);
//				p0 = points.get(lastPoint-1);
//				
//				for (int frontier = 1; frontier < lastFrontier; frontier++){
//					
//					if (
//							(p1.distance(where) - frontiers[frontier])
//							* (p0.distance(where) - frontiers[frontier])
//							< 0
//					){
//						
//						currentFrontier = frontier;
//						
//						if (p1.distance(where) > frontiers[frontier])
//							lastPointFrontier = frontier;
//						else
//							lastPointFrontier = frontier-1;
//						
//						double startX = .5*p1.getX() + .5*p0.getX();
//						double startY = .5*p1.getY() + .5*p0.getY();
//						
//						currentMark = menu.getCanvas().newPolyLine(startX, startY);
//						currentMark.lineTo(p1)
//									.setFilled(false)
//									.setOutlinePaint(((DEMM)menu).getColors()[lastPointFrontier])
//									.setStroke(mark.getStroke())
//									
//									.setDrawable(true)
//									.setPickable(false);
//						
//						marks.add(currentMark);
//						
//						break;
//					}
//				}
			}
			
		};
		
		public Transition release = new Release(">> " + INACTIVE_STATE){
			
			@Override
			public void action() {
				
				endSelection();
			}
			
		};
		
		/*
		 * The only way of finishing a selection is to perform a Release() in the selection state.
		 * If selectionEnded is modified in the Release.action(), then the criterion will have to
		 * wait for another mouse event to detect the end of the selection.
		 * See the SelectionEnded criterion.
		 */
		@Override
		public void enter() {
			selectionEnded = true;
		}
		
		@Override
		public void leave() {
		}
		
	};
	
	
	
	
	/**
	 * @param canvas
	 * @param menu
	 */
	public DEMMStateMachine(Canvas canvas, AbstractMenu menu, double[] frontiers) {

		//attachTo(canvas, true);
		super(canvas);
		
		this.menu = menu;
		
		this.gesture = new Gesture();
		
		this.mark = menu.getCanvas().newPolyLine();
		this.mark.setAntialiased(true)
				.setFilled(false)
				.setOutlinePaint(Color.blue)
				.setStroke(new BasicStroke(3))
				.setDrawable(false)
				.setPickable(false)
				.addTag(Trial.UtilTag);
		
		this.initialState = this.currentState = inactive;
		
		this.frontiers = frontiers;
		
		marks = new ArrayList<CPolyLine>();
		
	}

	/**
	 * @param canvas
	 * @param menu
	 * @param treshold
	 */
	public DEMMStateMachine(Canvas canvas, AbstractMenu menu, double[] frontiers, int treshold) {
		this(canvas, menu, frontiers);
		this.startTreshold = treshold;
	}
	
	/**
	 * Method calling the ghost() method of the specified item.
	 * @param where
	 * @param selected
	 */
	protected void callGhost(Point2D where, AbstractItem selected){
		
		if (selected != null && where != null)
			selected.ghost(where);
		else {
			if (selected == null)
				System.out.println("selected null");
			if (where == null)
				System.out.println("where null");
		}
		
	}
	
	/**
	 * Method calling the ghost() method of the selected item.
	 * @param where
	 */
	protected void callGhost(Point2D where){
		callGhost(where, selected);
	}
	
	public boolean isSelectionEnded(){
		return selectionEnded;
	}
	
	public CPolyLine getMark(){
		return mark;
	}
	
	public long[] getSelectionTimes(){
		return new long[]{selectionStart, selectionDuration};
	}
	
	public void resetSelection(){
		selectionEnded = false;
	}
	
	@Override
	public void doSuspend() {
		endSelection();
	}

	public void endSelection() {
		
		currentMark = null;

		selected = menu.getItem(gesture);
		
		callGhost(where);
		
		for (CPolyLine m : marks){
			m.setDrawable(false);
		}
		
		marks.clear();
		
		selectionDuration = System.currentTimeMillis() - selectionStart;
		
		((Trial)Platform.getExperiment().getCurrentBlock()).setMeasureValues();
		
	}

}
